﻿//===============================================================================
// Jammer.NET Development Library
// Jmr.Entity.Poco.Silverlight
//===============================================================================
// Copyright © Junaid Fakhruddin, Matthew Leedham and Randolph Cabral.  
// All rights reserved.
//===============================================================================
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
using Jmr.Compression;
using Jmr.Serialization;

namespace Jmr.Entity.Poco.Silverlight
{
	/// <summary>
	/// IBOXIEntitySessionProxy interface.
	/// </summary>
	public interface IBOXIEntitySessionProxy
	{
		/// <summary>
		/// IBOXIEntitySessionProxy.OnFetchReportFoldersCompleted event.
		/// </summary>
		event FetchReportFoldersCompletedEventHandler OnFetchReportFoldersCompleted;

		/// <summary>
		/// IBOXIEntitySessionProxy.OnFetchReportInstancesCompleted event.
		/// </summary>
		event FetchReportInstancesCompletedEventHandler OnFetchReportInstancesCompleted;

		/// <summary>
		/// IBOXIEntitySessionProxy.OnFetchReportInstancePromptsCompleted event.
		/// </summary>
		event FetchReportInstancePromptsCompletedEventHandler OnFetchReportInstancePromptsCompleted;

		/// <summary>
		/// IBOXIEntitySessionProxy.OnFetchReportsCompleted event.
		/// </summary>
		event FetchReportsCompletedEventHandler OnFetchReportsCompleted;

		/// <summary>
		/// IBOXIEntitySessionProxy.OnScheduleReportsCompleted event.
		/// </summary>
		event ScheduleReportsCompletedEventHandler OnScheduleReportsCompleted;

		/// <summary>
		/// IBOXIEntitySessionProxy.OnScheduleReportsCompleted event.
		/// </summary>
		event EnqueueToDatabaseCompletedEventHandler OnEnqueueToDatabaseCompleted;

		/// <summary>
		/// IBOXIEntitySessionProxy.OnDeleteInstancesCompleted event.
		/// </summary>
		event DeleteInstancesCompletedEventHandler OnDeleteInstancesCompleted;

		/// <summary>
		/// IBOXIEntitySessionProxy.OnScheduleReportsCompleted method.
		/// </summary>
		/// <typeparam name="TEntity">Type of entity.</typeparam>
		/// <param name="boxiSqlString">Query string.</param>
		/// <param name="userName">User name.</param>
		void FetchReportFolders<TEntity>(string boxiSqlString, string userName);

		/// <summary>
		/// IBOXIEntitySessionProxy.FetchReportInstances method.
		/// </summary>
		/// <typeparam name="TEntity">Type of entity.</typeparam>
		/// <param name="boxiSqlString">Query string.</param>
		/// <param name="userName">User name.</param>
		void FetchReportInstances<TEntity>(string boxiSqlString, string userName);

		/// <summary>
		/// IBOXIEntitySessionProxy.FetchInstancePrompts method.
		/// </summary>
		/// <typeparam name="TEntity">Type of entity.</typeparam>
		/// <param name="boxiSqlString">Query string.</param>
		/// <param name="userName">User name.</param>
		void FetchInstancePrompts<TEntity>(string boxiSqlString, string userName);

		/// <summary>
		/// IBOXIEntitySessionProxy.FetchReports method.
		/// </summary>
		/// <typeparam name="TEntity">Type of entity.</typeparam>
		/// <param name="boxiSqlString">Query string.</param>
		/// <param name="userName">User name.</param>
		void FetchReports<TEntity>(string boxiSqlString, string userName);

		/// <summary>
		/// IBOXIEntitySessionProxy.ScheduleReports method.
		/// </summary>
		/// <typeparam name="TEntity">Type of entity.</typeparam>
		/// <param name="boxiSqlString">Query string.</param>
		/// <param name="reportParameters">Report parameters.</param>
		void ScheduleReports<TEntity>(string boxiSqlString, IEnumerable<TEntity> reportParameters);

		/// <summary>
		/// IBOXIEntitySessionProxy.ScheduleReports method.
		/// </summary>
		/// <typeparam name="TEntity">Type of entity.</typeparam>
		/// <param name="boxiSqlString">Query string.</param>
		/// <param name="userName">User name.</param>
		/// <param name="reportParameters">Report parameters.</param>
		/// <param name="reportFormat">Report format.</param>
		void ScheduleReports<TEntity>(string boxiSqlString, string userName, IEnumerable<TEntity> reportParameters, int reportFormat);

		void EnqueueToDatabase<TEntity>(IEnumerable<TEntity> reportParameters, string userName, int reportId, int instanceId, string reportTitle, DateTime avgRunTime, int queuePriority, int reportFormat, int reportOriginalFormat);

		void DeleteInstances<TEntity>(string boxiSqlString, string userName);
	}

	public class BOXIEntitySessionFactory
	{

		public static IBOXIEntitySessionProxy CreateSession(string uri, string mappingassemblyfullname)
		{
			return new BOXIEntitySessionProxy(uri, mappingassemblyfullname);
		}
	}

		public delegate void FetchReportFoldersCompletedEventHandler(FetchResultEventArgs e);
		public delegate void FetchReportInstancesCompletedEventHandler(FetchResultEventArgs e);
		public delegate void FetchReportInstancePromptsCompletedEventHandler(FetchResultEventArgs e);
		public delegate void FetchReportsCompletedEventHandler(FetchResultEventArgs e);
		public delegate void ScheduleReportsCompletedEventHandler();
		public delegate void EnqueueToDatabaseCompletedEventHandler();
		public delegate void DeleteInstancesCompletedEventHandler();

		public class FetchResultEventArgs
		{
			public IEnumerable Results { get; set; }
		}

		public class BOXIEntitySessionProxy : IBOXIEntitySessionProxy
		{
			private SynchronizationContext _syncContext;
			private string _uristring = null;
			private string _mappingassemblyfullname = null;
			private Uri _uri = null;
			private EntityFormatter _formatter = null;
			private BinaryEntityFormatter _binaryformatter = null;			
			
			public BOXIEntitySessionProxy(string uri, string mappingassemblyfullname) 
			{
				_mappingassemblyfullname = mappingassemblyfullname;
				_uristring = uri.ToLower();
				_uri = new Uri(_uristring);
				_binaryformatter = new BinaryEntityFormatter();
				_syncContext = SynchronizationContext.Current;
			}

			private WebRequest GetWebRequest(BoxiAction action, string entityassemblyname, string entityfullname, object entityarg, string boxiSqlString)
			{
				var webrequest = HttpWebRequest.Create(_uri);
				webrequest.Headers["Culture"] = System.Globalization.CultureInfo.CurrentCulture.ToString();
				webrequest.Headers["action"] = action.ToString();
				webrequest.Headers["mappingassemblyfullname"] = _mappingassemblyfullname;
				webrequest.Headers["entityfullname"] = entityfullname;
				webrequest.Headers["entityassemblyname"] = entityassemblyname;
				webrequest.Headers["entityargfullname"] = (entityarg == null) ? "NULL" : entityarg.GetType().FullName;
				//webrequest.Headers["entityarg"] = (entityarg == null) ? "NULL" : Convert.ToBase64String(_binaryformatter.Serialize(entityarg).ToArray());
				webrequest.Headers["boxiSqlString"] = boxiSqlString;
				webrequest.Method = "POST";
				return webrequest;
			}

			private WebRequest GetWebRequest(BoxiAction action, string entityassemblyname, string entityfullname, object entityarg, string userName, string boxiSqlString, int reportFormat)
			{
				var webrequest = HttpWebRequest.Create(_uri);
				webrequest.Headers["Culture"] = System.Globalization.CultureInfo.CurrentCulture.ToString();
				webrequest.Headers["action"] = action.ToString();
				webrequest.Headers["rptFormat"] = reportFormat.ToString();
				webrequest.Headers["mappingassemblyfullname"] = _mappingassemblyfullname;
				webrequest.Headers["entityassemblyname"] = entityassemblyname;
				webrequest.Headers["entityfullname"] = entityfullname;
				webrequest.Headers["userName"] = userName;
				webrequest.Headers["entityargfullname"] = (entityarg == null) ? "NULL" : entityarg.GetType().FullName;
				//webrequest.Headers["entityarg"] = (entityarg == null) ? "NULL" : Convert.ToBase64String(_binaryformatter.Serialize(entityarg).ToArray());
				webrequest.Headers["boxiSqlString"] = boxiSqlString;
				webrequest.Method = "POST";
				return webrequest;
			}

			private WebRequest GetWebRequest(BoxiAction action, string entityassemblyname, string entityfullname, object entityarg, string userName, int reportId, int instanceId, string reportTitle, DateTime avgRunTime, int queuePriority, int reportFormat, int reportOriginalFormat)
			{
				var webrequest = HttpWebRequest.Create(_uri);
				webrequest.Headers["Culture"] = System.Globalization.CultureInfo.CurrentCulture.ToString();
				webrequest.Headers["action"] = action.ToString();
				webrequest.Headers["rptFormat"] = reportFormat.ToString();
				webrequest.Headers["reportId"] = reportId.ToString();
				webrequest.Headers["instanceId"] = instanceId.ToString();
				webrequest.Headers["avgRunTime"] = avgRunTime.ToString();
				webrequest.Headers["queuePriority"] = queuePriority.ToString();
				webrequest.Headers["reportOriginalFormat"] = reportOriginalFormat.ToString();
				webrequest.Headers["mappingassemblyfullname"] = _mappingassemblyfullname;
				webrequest.Headers["entityassemblyname"] = entityassemblyname;
				webrequest.Headers["entityfullname"] = entityfullname;
				webrequest.Headers["userName"] = userName;
				webrequest.Headers["reportTitle"] = reportTitle;
				webrequest.Headers["entityargfullname"] = (entityarg == null) ? "NULL" : entityarg.GetType().FullName;
				webrequest.Method = "POST";
				//webrequest.Headers["entityarg"] = (entityarg == null) ? "NULL" : Convert.ToBase64String(_binaryformatter.Serialize(entityarg).ToArray());
				return webrequest;
			}

			private WebRequest GetWebRequest(BoxiAction action, string entityassemblyname, string entityfullname, object entityarg, string userName, string boxiSqlString)
			{
				var webrequest = HttpWebRequest.Create(_uri);
				webrequest.Headers["Culture"] = System.Globalization.CultureInfo.CurrentCulture.ToString();
				webrequest.Headers["action"] = action.ToString();
				webrequest.Headers["mappingassemblyfullname"] = _mappingassemblyfullname;
				webrequest.Headers["entityassemblyname"] = entityassemblyname;
				webrequest.Headers["entityfullname"] = entityfullname;
				webrequest.Headers["userName"] = userName;
				webrequest.Headers["entityargfullname"] = (entityarg == null) ? "NULL" : entityarg.GetType().FullName;
				//webrequest.Headers["entityarg"] = (entityarg == null) ? "NULL" : Convert.ToBase64String(_binaryformatter.Serialize(entityarg).ToArray());
				webrequest.Headers["boxiSqlString"] = boxiSqlString;
				webrequest.Method = "POST";
				return webrequest;
			}
			#region IBOXIEntitySessionProxy Members

			public event FetchReportFoldersCompletedEventHandler OnFetchReportFoldersCompleted;
			public event FetchReportInstancesCompletedEventHandler OnFetchReportInstancesCompleted;
      public event FetchReportInstancePromptsCompletedEventHandler OnFetchReportInstancePromptsCompleted;
			public event FetchReportsCompletedEventHandler OnFetchReportsCompleted;
			public event ScheduleReportsCompletedEventHandler OnScheduleReportsCompleted;
			public event EnqueueToDatabaseCompletedEventHandler OnEnqueueToDatabaseCompleted;
      public event DeleteInstancesCompletedEventHandler OnDeleteInstancesCompleted;
			public static event ExceptionEventHandler OnBoxiException;


			public void FetchReportFolders<TEntity>(string boxiSqlString, string userName)
			{
				DoAction<TEntity>(BoxiAction.FetchFolders, null,userName, boxiSqlString);
			}

			public void ScheduleReports<TEntity>(string boxiSqlString, IEnumerable<TEntity> entities)
			{
				DoAction<TEntity>(BoxiAction.ScheduleReports, entities, boxiSqlString);
			}

			public void ScheduleReports<TEntity>(string boxiSqlString, string userName, IEnumerable<TEntity> entities, int reportFormat)
			{
				DoAction<TEntity>(BoxiAction.ScheduleReports, entities, userName, boxiSqlString, reportFormat);
			}

			public void FetchReportInstances<TEntity>(string boxiSqlString, string userName)
			{
				DoAction<TEntity>(BoxiAction.FetchInstances, null,userName, boxiSqlString);
			}

			public void FetchInstancePrompts<TEntity>(string boxiSqlString, string userName)
			{
				DoAction<TEntity>(BoxiAction.FetchInstancePrompts, null, userName, boxiSqlString);
			}

			public void FetchReports<TEntity>(string boxiSqlString, string userName)
			{
				DoAction<TEntity>(BoxiAction.FetchReports, null, userName, boxiSqlString);
			}

			public void EnqueueToDatabase<TEntity>(IEnumerable<TEntity> reportParameters, string userName, int reportId, int instanceId, string reportTitle, DateTime avgRunTime, int queuePriority, int reportFormat, int reportOriginalFormat)
			{
				DoAction<TEntity>(BoxiAction.EnqueueToDatabase, reportParameters, userName, reportId, instanceId, reportTitle, avgRunTime, queuePriority, reportFormat, reportOriginalFormat);
			}

			public void DeleteInstances<TEntity>(string boxiSqlString, string userName)
			{
				DoAction<TEntity>(BoxiAction.DeleteInstances, null, userName, boxiSqlString);
			}

			public void DoAction<TEntity>(BoxiAction action, object entityArg, string boxiSqlString)
			{
				var fetchSetWebRequest = GetWebRequest(action, typeof(TEntity).Assembly.FullName, typeof(TEntity).FullName,entityArg, boxiSqlString);
				//var asyncresult = fetchSetWebRequest.BeginGetResponse(new AsyncCallback(BinaryActionCompleted), fetchSetWebRequest);
				var entityarg = (entityArg == null) ? new byte[0] : _binaryformatter.Serialize(entityArg).ToArray();
				fetchSetWebRequest.BeginGetRequestStream(new AsyncCallback(ReadCallback), new EntityArgRequest { EntityArg = entityarg, Request = fetchSetWebRequest });
			}

			public void DoAction<TEntity>(BoxiAction action, object entityArg, string userName, string boxiSqlString)
			{
				var fetchSetWebRequest = GetWebRequest(action, typeof(TEntity).Assembly.FullName, typeof(TEntity).FullName, entityArg,userName, boxiSqlString);
				//var asyncresult = fetchSetWebRequest.BeginGetResponse(new AsyncCallback(BinaryActionCompleted), fetchSetWebRequest);
				var entityarg = (entityArg == null) ? new byte[0] : _binaryformatter.Serialize(entityArg).ToArray();
				fetchSetWebRequest.BeginGetRequestStream(new AsyncCallback(ReadCallback), new EntityArgRequest { EntityArg = entityarg, Request = fetchSetWebRequest });
			}

			public void DoAction<TEntity>(BoxiAction action, object entityArg, string userName, string boxiSqlString, int reportFormat)
			{
				var fetchSetWebRequest = GetWebRequest(action, typeof(TEntity).Assembly.FullName, typeof(TEntity).FullName, entityArg, userName, boxiSqlString, reportFormat);
				//var asyncresult = fetchSetWebRequest.BeginGetResponse(new AsyncCallback(BinaryActionCompleted), fetchSetWebRequest);
				var entityarg = (entityArg == null) ? new byte[0] : _binaryformatter.Serialize(entityArg).ToArray();
				fetchSetWebRequest.BeginGetRequestStream(new AsyncCallback(ReadCallback), new EntityArgRequest { EntityArg = entityarg, Request = fetchSetWebRequest });

			}

			public void DoAction<TEntity>(BoxiAction action, object entityArg, string userName, int reportId, int instanceId, string reportTitle, DateTime avgRunTime, int queuePriority, int reportFormat, int reportOriginalFormat)
			{
				var enqueueToDatabaseWebRequest = GetWebRequest(action, typeof(TEntity).Assembly.FullName, typeof(TEntity).FullName, entityArg, userName, reportId, instanceId, reportTitle, avgRunTime, queuePriority, reportFormat, reportOriginalFormat);
				//var asyncresult = enqueueToDatabaseWebRequest.BeginGetResponse(new AsyncCallback(BinaryActionCompleted), enqueueToDatabaseWebRequest);
				var entityarg = (entityArg == null) ? new byte[0] : _binaryformatter.Serialize(entityArg).ToArray();
				enqueueToDatabaseWebRequest.BeginGetRequestStream(new AsyncCallback(ReadCallback), new EntityArgRequest { EntityArg = entityarg, Request = enqueueToDatabaseWebRequest });
			}

			private void ReadCallback(IAsyncResult asynchronousResult)
			{
				var argRequest = (EntityArgRequest)asynchronousResult.AsyncState;
				var entityarg = argRequest.EntityArg;
				var request = argRequest.Request;
				var postStream = request.EndGetRequestStream(asynchronousResult);
				postStream.Write(entityarg, 0, entityarg.Length);
				postStream.Flush();
				postStream.Close();
				var asyncresult = request.BeginGetResponse(new AsyncCallback(BinaryActionCompleted), request);
			}

			public void RaiseException(JmrSerializationException exception)
			{
				if (OnBoxiException != null)
					OnBoxiException(exception);
			}

			void BinaryActionCompleted(IAsyncResult result)
			{
				HttpWebRequest request = (HttpWebRequest)result.AsyncState;
				var action = (BoxiAction)Enum.Parse(typeof(BoxiAction), request.Headers["action"], true);
				switch (action)
				{
					case BoxiAction.FetchFolders:
						_syncContext.Post(FetchFolders_BinaryActionCompleted, result);
						break;
					case BoxiAction.FetchReports:
						_syncContext.Post(FetchReports_BinaryActionCompleted, result);
						break;
					case BoxiAction.FetchInstances:
						_syncContext.Post(FetchInstances_BinaryActionCompleted, result);
						break;
					case BoxiAction.ScheduleReports:
						_syncContext.Post(ScheduleReports_BinaryActionCompleted, result);
						break;
					case BoxiAction.FetchInstancePrompts:
						_syncContext.Post(FetchInstancePrompts_BinaryActionCompleted, result);
						break;
					case BoxiAction.EnqueueToDatabase:
						_syncContext.Post(EnqueueToDatabase_BinaryActionCompleted, result);
						break;
					case BoxiAction.DeleteInstances:
						_syncContext.Post(DeleteInstances_BinaryActionCompleted, result);
						break;

				}
			}

			HttpWebRequest GetRequest(IAsyncResult result)
			{
				return (HttpWebRequest)result.AsyncState;
			}

			HttpWebResponse GetResponse(IAsyncResult result)
			{
				var request = GetRequest(result);
				return (HttpWebResponse)request.EndGetResponse(result);
			}

			Stream GetResponseStream(IAsyncResult result)
			{
				var response = GetResponse(result);
				return response.GetResponseStream();
			}

			int ParseRecordsAffectedAsyncResult(IAsyncResult result)
			{
				var stream = GetResponseStream((IAsyncResult)result);
				return ParseRecordsAffectedStream(stream);
			}

			int ParseRecordsAffectedStream(Stream stream)
			{
				var streamreader = new StreamReader(stream);
				return Convert.ToInt32(streamreader.ReadToEnd());
			}

			void ScheduleReports_BinaryActionCompleted(object state)
			{
				if (OnScheduleReportsCompleted != null)
					OnScheduleReportsCompleted();
			}

			void EnqueueToDatabase_BinaryActionCompleted(object state)
			{
				if (OnEnqueueToDatabaseCompleted != null)
					OnEnqueueToDatabaseCompleted();
			}

			void DeleteInstances_BinaryActionCompleted(object state)
			{
				if (OnDeleteInstancesCompleted != null)
					OnDeleteInstancesCompleted();
			}

			private void FetchFolders_BinaryActionCompleted(object state)
			{
				object deserializedObj = GetDeserializedObject(state);

				if (deserializedObj is JmrSerializationException)
				{
          RaiseException((JmrSerializationException)deserializedObj);
					return;
				}

				if (OnFetchReportFoldersCompleted != null)
					OnFetchReportFoldersCompleted(new FetchResultEventArgs { Results = (IEnumerable)deserializedObj });
			}

			private void FetchReports_BinaryActionCompleted(object state)
			{
				object deserializedObj = GetDeserializedObject(state);

				if (deserializedObj is JmrSerializationException)
				{
					RaiseException((JmrSerializationException)deserializedObj);
					return;
				}

				if (OnFetchReportsCompleted != null)
					OnFetchReportsCompleted(new FetchResultEventArgs { Results = (IEnumerable)deserializedObj });
			}

			private void FetchInstances_BinaryActionCompleted(object state)
			{
				object deserializedObj = GetDeserializedObject(state);

				if (deserializedObj is JmrSerializationException)
				{
					RaiseException((JmrSerializationException)deserializedObj);
					return;
				}

				if (OnFetchReportInstancesCompleted != null)
					OnFetchReportInstancesCompleted(new FetchResultEventArgs { Results = (IEnumerable)deserializedObj });
			}

			private void FetchInstancePrompts_BinaryActionCompleted(object state)
			{
				object deserializedObj = GetDeserializedObject(state);

				if (deserializedObj is JmrSerializationException)
				{
					RaiseException((JmrSerializationException)deserializedObj);
					return;
				}

				if (OnFetchReportInstancePromptsCompleted != null)
					OnFetchReportInstancePromptsCompleted(new FetchResultEventArgs { Results = (IEnumerable)deserializedObj });
			}

			private object GetDeserializedObject(object state)
			{
				HttpWebRequest request = GetRequest((IAsyncResult)state);
				HttpWebResponse response = GetResponse((IAsyncResult)state);

				var assemblyfullname = request.Headers["entityassemblyname"];
				var entityfullname = request.Headers["entityfullname"];
				var listtype = typeof(List<>).MakeGenericType(ParseType(assemblyfullname, entityfullname));
				var resp = response.GetResponseStream();
				StreamReader reader = new StreamReader(resp);

				var responseString = reader.ReadToEnd();
				var responseLength = int.Parse(responseString.Substring(0, responseString.IndexOf("@@@")).Replace("@@@", string.Empty));
				responseString = responseString.Substring(responseString.IndexOf("@@@") + 3);
				responseString = responseString.Substring(0, responseLength);

				var memstream = new MemoryStream(Convert.FromBase64String(responseString));
				var uncompresssed = Utility.DecompressHeaderless(memstream);
				var deserializedObj = _binaryformatter.Deserialize(listtype, uncompresssed);
				return deserializedObj;
			}

			protected Type ParseType(string assemblyfullname, string entityfullname)
			{
				Type retval = default(Type);
				retval = System.Reflection.Assembly.Load(assemblyfullname).GetType(entityfullname);
				return retval;
			}
			#endregion
		}
	}
